package com.ruoyi.workbench.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.constant.WorkBenchConstants;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.workbench.domain.WbContract;
import com.ruoyi.workbench.domain.sms.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.TreeMap;

/**
 * @author hanjiafeng
 * @date 2020/10/20 9:44
 * @desc 短信发送类
 */
@Slf4j
public class SmsHelper {

    private static final String GRANT_TYPE = "client_credentials";
    private static final String APP_ID = "414000050000277364";
    private static final String APP_SECRET = "fe50e545cbb2374244418378c67ed2ac";
    private static final String TEMPLATE_ID = "91556831";
    private static AccessTokenEntity accessTokenEntity;
    private static long expired_time;

    public static SmsStatusEntity sendSms(WbContract contract, String phoneNumber, double month) throws UnsupportedEncodingException {

        HttpHeaders httpHeaders = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("application/x-www-form-urlencoded");
        httpHeaders.setContentType(type);

        TreeMap<String, Object> paramMap = new TreeMap<>();
        paramMap.put("param1", contract.getContractValue());
        paramMap.put("param2", "[" + contract.getContractName() + "]");
        paramMap.put("param3", month);
        JSONObject jsonObject = new JSONObject(paramMap);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String timestamp = simpleDateFormat.format(System.currentTimeMillis());
        timestamp = URLEncoder.encode(timestamp, "utf-8");
        String postBody = String.format("app_id=%s&access_token=%s&acceptor_tel=%s&template_id=%s&template_param=%s&timestamp=%s"
                , APP_ID, accessTokenEntity.getAccess_token(), phoneNumber, TEMPLATE_ID, jsonObject, timestamp);

        HttpEntity<String> httpEntity = new HttpEntity<>(postBody, httpHeaders);
        System.out.println(httpEntity);

        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        ResponseEntity<SmsStatusEntity> responseEntity = restTemplate.postForEntity("http://api.189.cn/v2/emp/templateSms/sendSms", httpEntity, SmsStatusEntity.class);
        System.out.println(responseEntity);
        return responseEntity.getBody();
    }

    /**
     * 查询短信发送状态
     *
     * @param identifier 发送短信回执
     */
    public static QuerySmsStatus querySmsStatus(String identifier) {

        HttpHeaders httpHeaders = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("application/x-www-form-urlencoded");
        httpHeaders.setContentType(type);

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String timestamp = simpleDateFormat.format(System.currentTimeMillis());
        TreeMap<String, String> paramsMap = new TreeMap<>();
        paramsMap.put("access_token", accessTokenEntity.getAccess_token());
        paramsMap.put("app_id", APP_ID);
        paramsMap.put("identifier", identifier);
        paramsMap.put("timestamp", timestamp);
        String params = ParamsSign.value(paramsMap, APP_SECRET);
        String postBody = String.format("app_id=%s&access_token=%s&identifier=%s&timestamp=%s&sign=%s"
                , APP_ID, accessTokenEntity.getAccess_token(), identifier, timestamp, params);

        HttpEntity<String> httpEntity = new HttpEntity<>(postBody, httpHeaders);
        System.out.println(httpEntity);

        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<QuerySmsStatus> responseEntity = restTemplate.postForEntity("http://api.189.cn/v2/EMP/nsagSms/appnotify/querysmsstatus", httpEntity, QuerySmsStatus.class);
        System.out.println(responseEntity);
        return responseEntity.getBody();
    }

    /**
     * 给所有号码发送短信
     *
     * @param contractList 合同列表
     * @param phoneList    号码本
     */
    public static void sendSmses(List<WbContract> contractList, List<WbPhone> phoneList) {
        initAccessToken();
        contractList.forEach(contract -> phoneList.forEach(wbPhone -> {
            //发送之前先校验access token，如果不存在或者过期，重新获取，最多3次
            int getAccessTokenCount = 0;
            for (; getAccessTokenCount < WorkBenchConstants.MAX_COUNT && (Objects.isNull(accessTokenEntity.getAccess_token()) || isAccessTokenExpired()); getAccessTokenCount++) {
                initAccessToken();
            }
            if (getAccessTokenCount == WorkBenchConstants.MAX_COUNT) {
                log.error("access token获取失败");
                return;
            }
            int sendSmsCount = 0;
            String phoneNumber = wbPhone.getPhoneNumber();
            double month = DateUtil.getMonths(contract.getContractEndDate());
            for (; sendSmsCount < WorkBenchConstants.MAX_COUNT; sendSmsCount++) {
                try {
                    //发送短信
                    SmsStatusEntity smsStatusEntity = sendSms(contract, phoneNumber, month);
                    Thread.sleep(1000 * 5);
                    //检查发送状态，如果发送成功，跳出循环
                    if (WorkBenchConstants.RES_CODE_SUCCESS.equals(smsStatusEntity.getRes_code())) {
                        QuerySmsStatus querySmsStatus = querySmsStatus(smsStatusEntity.getIdertifier());
                        if (querySmsStatus.getRes_code().equals(WorkBenchConstants.RES_CODE_SUCCESS)) {
                            break;
                        }
                    }
                } catch (InterruptedException | UnsupportedEncodingException e) {
                    log.error("[" + contract.getContractName() + "," + month + "]发送至[" + phoneNumber + "]失败:" + e.getMessage());
                }
            }
            if (sendSmsCount == WorkBenchConstants.MAX_COUNT) {
                log.error("[" + contract.getContractName() + "," + month + "]发送至[" + wbPhone.getPhoneNumber() + "]超时");
            } else {
                log.info("[" + contract.getContractName() + "," + month + "]发送至[" + wbPhone.getPhoneNumber() + "]成功");
            }
        }));
    }

    public static void sendSmses(List<WbContract> contractList) {
        initAccessToken();
        contractList.forEach(contract -> {
            //发送之前先校验access token，如果不存在或者过期，重新获取，最多3次
            int getAccessTokenCount = 0;
            for (; getAccessTokenCount < WorkBenchConstants.MAX_COUNT && (Objects.isNull(accessTokenEntity.getAccess_token()) || isAccessTokenExpired()); getAccessTokenCount++) {
                initAccessToken();
            }
            if (getAccessTokenCount >= WorkBenchConstants.MAX_COUNT) {
                log.error("access token获取失败");
                return;
            }
            String operatorPhoneNumber = contract.getOperatorPhoneNumber();
            String deptPhoneNumber = contract.getPhoneNumber();
            if (StringUtils.isEmpty(deptPhoneNumber)) {
                log.error("部门负责人号码为空，短信发送失败");
                return;
            }
            String[] phoneNumbers;
            if (deptPhoneNumber.equals(operatorPhoneNumber)) {
                phoneNumbers = new String[]{deptPhoneNumber};
            } else {
                phoneNumbers = new String[]{operatorPhoneNumber, deptPhoneNumber};
            }
            Random random = new Random();
            for (String phoneNumber : phoneNumbers) {
                int sendSmsCount = 0;
                double month = DateUtil.getMonths(contract.getContractEndDate());
                for (; sendSmsCount < WorkBenchConstants.MAX_COUNT; sendSmsCount++) {
                    try {
                        //发送短信
//                        SmsStatusEntity smsStatusEntity = sendSms(contract, phoneNumber, month);
                        log.info("发送短信到" + phoneNumber);
                        Thread.sleep(1000 * 5);
                        log.info("检测发送状态");
                        int tt = random.nextInt(100);
                        if (tt >= 10) {
                            break;
                        }
                        //检查发送状态，如果发送成功，跳出循环
                        /*if (WorkBenchConstants.RES_CODE_SUCCESS.equals(smsStatusEntity.getRes_code())) {
                            QuerySmsStatus querySmsStatus = querySmsStatus(smsStatusEntity.getIdertifier());
                            if (querySmsStatus.getRes_code().equals(WorkBenchConstants.RES_CODE_SUCCESS)) {
                                break;
                            }
                        }*/
                    } catch (InterruptedException e) {
                        log.error("[" + contract.getContractName() + "," + month + "]发送至[" + phoneNumber + "]失败:" + e.getMessage());
                    }
                }
                if (sendSmsCount == WorkBenchConstants.MAX_COUNT) {
                    log.error("[" + contract.getContractName() + "," + month + "]发送至[" + phoneNumber + "]超时");
                } else {
                    log.info("[" + contract.getContractName() + "," + month + "]发送至[" + phoneNumber + "]成功");
                }
            }
        });
    }

    /**
     * 检查当前短信验证码剩余数量
     */
    public static SmsSufficient selectLeft() {
        HttpHeaders httpHeaders = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("application/x-www-form-urlencoded");
        httpHeaders.setContentType(type);

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String timestamp = simpleDateFormat.format(System.currentTimeMillis());
        try {
            timestamp = URLEncoder.encode(timestamp, "utf-8");
        } catch (UnsupportedEncodingException e) {
            log.error("编码格式异常:" + e.getMessage());
        }
        String postBody = String.format("app_id=%s&access_token=%s&timestamp=%s"
                , APP_ID, accessTokenEntity.getAccess_token(), timestamp);
        HttpEntity<String> httpEntity = new HttpEntity<>(postBody, httpHeaders);
        System.out.println(httpEntity);

        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://api.189.cn/v2/emp/templateSms/pleft", httpEntity, String.class);
        return JSON.toJavaObject(JSON.parseObject(responseEntity.getBody()), SmsSufficient.class);
    }

    /**
     * 获取access token
     */
    public static void initAccessToken() {
        RestTemplate restTemplate = new RestTemplate();
        String url = String.format("https://oauth.api.189.cn/emp/oauth2/v3/access_token?grant_type=%s&app_id=%s&app_secret=%s", GRANT_TYPE, APP_ID, APP_SECRET);
        try {
            ResponseEntity<AccessTokenEntity> responseEntity = restTemplate.postForEntity(url, null, AccessTokenEntity.class);
            accessTokenEntity = responseEntity.getBody();
            if (Objects.isNull(accessTokenEntity)) {
                throw new HttpClientErrorException(HttpStatus.EXPECTATION_FAILED, "未解析到access token:" + responseEntity);
            }
            if (!"0".equals(accessTokenEntity.getRes_code())) {
                throw new HttpClientErrorException(HttpStatus.NO_CONTENT, "未解析到access token:" + responseEntity);
            }
            expired_time = System.currentTimeMillis() + accessTokenEntity.getExpires_in();
        } catch (HttpClientErrorException e) {
            log.error("[Access Token]请求错误：" + e.getMessage());
            accessTokenEntity = null;
        }
    }

    /**
     * access token是否过期
     *
     * @return 是否过期
     */
    public static boolean isAccessTokenExpired() {
        return System.currentTimeMillis() > expired_time;
    }
}
